Skip to content

[Feature/#108] 루틴 리스트 화면을 구현합니다.#111

Merged
wjdrjs00 merged 21 commits intodevelopfrom
feature/#108-routine-list
Aug 18, 2025
Merged

[Feature/#108] 루틴 리스트 화면을 구현합니다.#111
wjdrjs00 merged 21 commits intodevelopfrom
feature/#108-routine-list

Conversation

@wjdrjs00
Copy link
Copy Markdown
Member

@wjdrjs00 wjdrjs00 commented Aug 17, 2025

[ PR Content ]

루틴 리스트 화면을 구현했습니다.

Related issue

Screenshot 📸

Screen_recording_20250818_031144.mp4

Work Description

  • 루틴 리스트 화면 구현
  • 루틴 삭제 변경된 api 버전에 맞게 수정

Uncompleted Tasks

  • 루틴 조회 api 추가될 필드 대응하기
    • 루틴 기간
    • 루틴 삭제여부
  • 삭제, 수정 완료 시
    • 루틴 갱신 로직 추가하기
    • 토스트 메시지 제공

To Reviewers 📢

  • 서버 api 미구현 상태로 인해 자잘한게 구현이 안된 사항들이 있습니다..(위에 정리한것들)
    간단한 작업들이기도 하고, 남은 시간이 별로 없어서 우선 구현이 완료된 부분을 먼저 올렸습니다.
  • 궁금한점, 질문 등등 리뷰 남겨주시면 답변하겠습니다요.

Summary by CodeRabbit

  • 신규 기능

    • 홈에서 선택한 날짜로 이동하는 “루틴 목록” 화면 추가 및 해당 화면 컨테이너/뷰모델 도입
    • 주간 날짜 선택 바(WeeklyDatePicker) 및 빈 상태 안내/루틴 등록 버튼 추가
  • 개선

    • 루틴 시간 표시에 24시간 포맷 적용 및 12시간 포맷 추가
    • 삭제 시 “오늘만 삭제” / “모든 날짜에서 삭제” 옵션 제공
  • UI/디자인

    • 루틴 상세 카드, 삭제/수정 확인 바텀시트 추가
    • 아이콘 여러 개 추가/수정, 디자인 시스템에 yellow10 색상 및 텍스트 버튼의 삭제 스타일 추가

@wjdrjs00 wjdrjs00 requested a review from l5x5l August 17, 2025 18:18
@wjdrjs00 wjdrjs00 self-assigned this Aug 17, 2025
@wjdrjs00 wjdrjs00 added ✨ Feature 새로운 기능 구현 🧤 대현 labels Aug 17, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Aug 17, 2025

Walkthrough

홈 화면에서 RoutineList로 내비게이션을 추가하고 루틴 리스트 화면(MVI) 전체 UI/뷰모델·상태·사이드이펙트·컴포넌트들을 구현했으며, 루틴 삭제 API/레포지토리/유즈케이스를 routineId 기반으로 리팩터링하고 디자인 시스템 색상·아이콘·버튼 프리셋 및 시간 포맷을 추가/수정했습니다.

Changes

Cohort / File(s) Summary
Navigation & routing
app/src/main/java/com/threegap/bitnagil/MainNavHost.kt, app/src/main/java/com/threegap/bitnagil/Route.kt, app/src/main/java/com/threegap/bitnagil/navigation/home/HomeNavHost.kt
Route.RoutineList 추가 및 NavHost에 등록. HomeNavHost 서명에 navigateToRoutineList 콜백 추가 및 라우팅 처리(lauchSingleTop).
Home presentation wiring
presentation/src/main/java/.../home/HomeScreen.kt, .../HomeViewModel.kt, .../model/HomeIntent.kt, .../model/HomeSideEffect.kt
HomeScreenContainer 서명 변경으로 navigateToRoutineList 주입, OnShowMoreRoutinesClick 인텐트 추가 및 NavigateToRoutineList 사이드이펙트 발생/처리 흐름 추가.
RoutineList feature (presentation)
presentation/src/main/java/.../routinelist/* (다수: RoutineListScreen.kt, RoutineListViewModel.kt, model/.kt, component/template/)
전체 RoutineList 화면 구현: 컨테이너, ViewModel(MVI), State/Intent/SideEffect, UI 모델(RoutineUiModel 등), WeeklyDatePicker, RoutineDetailsCard, Empty view, Delete/Edit 바텀시트 등 컴포넌트 추가.
Data layer: routine API / repo / datasource
data/.../routine/service/RoutineService.kt, data/.../datasource/RoutineRemoteDataSource.kt, data/.../datasourceImpl/RoutineRemoteDataSourceImpl.kt, data/.../repositoryImpl/RoutineRepositoryImpl.kt
delete-by-day DTO 방식 제거, 새로운 DELETE /api/v2/routines/day/{routineId}deleteRoutineForDay(routineId) 추가. 단건 조회 getRoutine(routineId) 추가. 원격/레포지토리/인터페이스 및 구현체 시그니처 정리.
Domain use-cases / API
domain/.../routine/repository/RoutineRepository.kt, domain/.../usecase/*
Repository 인터페이스에 deleteRoutineForDay(routineId) 추가, 기존 DeleteRoutineByDay 관련 use-case 삭제 및 DeleteRoutineForDayUseCase 추가.
Domain model change
domain/src/main/java/.../RecommendedRoutineType.kt
enum에서 UNKNOWN 제거 및 companion.fromString 반환을 nullable로 변경(RecommendedRoutineType?).
Design system — colors & components
core/designsystem/.../color/Color.kt, .../BitnagilColors.kt, .../BitnagilTextButton.kt
Yellow10 색상 상수 추가, BitnagilColors에 yellow10 추가, BitnagilTextButtonColor에 delete() 프리셋 추가.
Design system — drawables
core/designsystem/src/main/res/drawable/* (ic_connect.xml, ic_grow.xml, ic_outside.xml, ic_rest.xml, ic_shine.xml, ic_edit.xml, ic_trash.xml, ic_wakeup.xml)
다수 벡터 아이콘 추가 및 기존 아이콘 리디자인(캔버스 크기·채움/스트로크 변경).
Time formatting / home UI
presentation/.../home/component/template/RoutineSection.kt, presentation/.../home/util/LocalDateExtension.kt
시간 포맷 분리: formatExecutionTime24Hour(), formatExecutionTime12Hour() 추가. RoutineSection에서 24시간 포맷 사용. 자정 표기 변경("하루종일").

Sequence Diagram(s)

sequenceDiagram
actor User
participant HomeScreen
participant HomeVM
participant NavHost
participant RoutineListScreen
participant RoutineListVM

User->>HomeScreen: 날짜 선택/더보기 클릭
HomeScreen->>HomeVM: HomeIntent.OnShowMoreRoutinesClick
HomeVM-->>HomeScreen: HomeSideEffect.NavigateToRoutineList(selectedDate)
HomeScreen->>NavHost: navigate(Route.RoutineList(selectedDate), launchSingleTop=true)
NavHost->>RoutineListScreen: Compose RoutineListScreenContainer(selectedDate)
RoutineListScreen->>RoutineListVM: 초기화(저장된 selectedDate)
Loading
sequenceDiagram
participant RoutineListVM
participant FetchWeeklyUseCase
participant Repo
participant RemoteService

RoutineListVM->>FetchWeeklyUseCase: invoke(startDate,endDate)
FetchWeeklyUseCase->>Repo: fetchWeeklyRoutines(...)
Repo->>RemoteService: GET /api/v2/routines/weekly
RemoteService-->>Repo: RoutinesDto
Repo-->>FetchWeeklyUseCase: Routines
FetchWeeklyUseCase-->>RoutineListVM: 성공 -> 상태 업데이트

RoutineListVM->>Repo: deleteRoutineForDay(routineId)
Repo->>RemoteService: DELETE /api/v2/routines/day/{routineId}
RemoteService-->>Repo: 200 OK
Repo-->>RoutineListVM: 성공 -> 시트 닫기 / 로딩 해제
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Assessment against linked issues

Objective Addressed Explanation
루틴 리스트 화면 UI 구현 (#108)
루틴 리스트 화면 API 연동 (#108)

Out-of-scope changes

Code Change Explanation
디자인 시스템 색상 추가: Yellow10 및 BitnagilColors.yellow10 (core/designsystem/.../color/Color.kt, .../BitnagilColors.kt) 루틴 리스트 화면 구현/API 연동 이슈(#108)에 직접 명시된 범위가 아니며 전역 디자인 팔레트 확장임.
다수 아이콘 추가/리디자인: ic_edit.xml, ic_trash.xml, ic_connect.xml 등 (core/designsystem/src/main/res/drawable/...) UI 자산(아이콘) 추가·수정은 기능적 요구사항 범위를 벗어나며 그래픽 리소스 작업으로 분류됨.
도메인 enum 변경: RecommendedRoutineType에서 UNKNOWN 제거 및 fromString nullable 전환 (domain/.../RecommendedRoutineType.kt) 루틴 리스트 화면/API 요구사항과 직접 명시적으로 연관되지 않으며 전역 영향(호출부 변경)이 우려됨.

Possibly related PRs

Suggested reviewers

  • l5x5l

Poem

토끼가 껑충, 캘린더 위를 건너네 🐇
노란빛 아이콘에 루틴들이 반짝✨
오늘만? 모두? 바텀시트가 속삭이면
탭 한 번에 뒤로가고 다음 날짜로 총총
작은 버튼에 큰 변화, 토끼가 박수 쳐요👏

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/#108-routine-list

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (43)
presentation/src/main/java/com/threegap/bitnagil/presentation/home/model/HomeSideEffect.kt (1)

10-10: 날짜 전달은 포맷 고정이 관건 — String 유지 시 ISO 고정, 또는 타입 강화 고려

selectedDate를 String으로 두면 직렬화(특히 Route의 @serializable) 측면에서 간편하지만, 포맷 흔들림(로케일/패턴)이 생길 수 있습니다. 두 가지 중 하나를 권장드립니다:

  • String 유지: 한 곳에서 ISO_LOCAL_DATE로 고정 변환/파싱 유틸 제공
  • 타입 강화(선호): 사이드이펙트는 LocalDate로 들고 가고, 실제 네비게이션 직전에서만 ISO String으로 변환

후자를 택하면 사이드이펙트 경계에서 도메인 의미를 지키기 쉽습니다. 다만 Route가 kotlin.serialization을 쓰는 경우 LocalDate는 기본 지원이 없어(커스텀 serializer 필요) Route 쪽은 String 유지가 현실적입니다.

선택지 1(현행 유지) — 유틸 추가 예시:

import java.time.LocalDate
import java.time.format.DateTimeFormatter

private val ROUTE_DATE_FMT = DateTimeFormatter.ISO_LOCAL_DATE
fun LocalDate.toRouteArg(): String = format(ROUTE_DATE_FMT)
fun String.toLocalDateOrNull(): LocalDate? =
    runCatching { LocalDate.parse(this, ROUTE_DATE_FMT) }.getOrNull()

선택지 2(타입 강화) — 사이드이펙트만 LocalDate로 변경 예시:

-    data class NavigateToRoutineList(val selectedDate: String) : HomeSideEffect
+    data class NavigateToRoutineList(val selectedDate: LocalDate) : HomeSideEffect

이 경우 import 추가가 필요합니다:

import java.time.LocalDate

위 포맷(ISO_LOCAL_DATE)이 ViewModel → Route 생성까지 일관적으로 사용되는지 점검 부탁드립니다.

core/designsystem/src/main/res/drawable/ic_connect.xml (1)

1-50: 브랜드 컬러 하드코딩과 아이콘 사이즈 정책 점검 제안

  • 색상: 채움/스트로크에 #715DF1이 하드코딩되어 있어 테마/다크모드/틴트 적용 시 유연성이 떨어질 수 있습니다. 디자인 토큰(@color/...)으로 치환하거나, 소비처에서 tint를 적용할 계획이면 채움은 투명/스트로크만 두고 코드에서 tint로 일괄 제어하는 방식을 고려해 주세요.
  • 사이즈: 본 아이콘은 24dp 뷰포트인데, 동일 PR 내 ic_edit 등은 20dp입니다. 소비처(Composable)에서 Modifier.size로 강제 크기를 일관 적용한다면 문제 없지만, 인트린식 사이즈에 의존하는 곳이 있다면 시각적 불균형이 생길 수 있어 정책 확인을 권장합니다.

필요 시 색상 자원화나 tint 적용 가이드를 정리해 두면 이후 아이콘 추가/수정 시 유지보수성이 좋아집니다.

core/designsystem/src/main/res/drawable/ic_wakeup.xml (1)

1-76: 오렌지(#FE7120) 하드코딩 → 디자인 토큰/틴트 전략 합의 권장

ic_connect.xml과 동일한 맥락입니다.

  • 색상: 스트로크/채움이 고정 HEX여서 테마별 색상 제어나 공용 tint 적용이 어려울 수 있습니다. @color 토큰으로 치환하거나 코드 tint 전략으로 통일하는 방안을 검토해 주세요.
  • 성능: path가 다수이지만 24px급 벡터에서 영향은 미미합니다. 다만 동일 도형의 채움+외곽선 중복이 많은 경우에는 필요 최소 세트로 정리해도 됩니다(선택 사항).

디자인 시스템 차원의 아이콘/색상 가이드를 문서화해 두면 이후 자산 추가 시 품질이 일정하게 유지됩니다.

core/designsystem/src/main/res/drawable/ic_edit.xml (1)

13-21: 색상 하드코딩(#989BA2) — 토큰화 또는 tint 전략으로 일원화 제안

  • 채움/스트로크에 동일한 회색을 중복 명시하고 있습니다. 디자인 시스템의 중립색 토큰(@color/neutralXX 등)이 있다면 해당 자원으로 교체를 추천합니다.
  • 코드 측 tint로 색을 통제할 계획이라면 채움은 투명으로 두고 스트로크만 남기거나, 반대로 채움만 남겨 단일 경로로 단순화하는 방법도 있습니다(선택).

외곽선+채움 2중 경로는 의도된 스타일로 보이며, 유지하고자 한다면 색상 참조만 자원화해 주세요.

core/designsystem/src/main/res/drawable/ic_shine.xml (2)

7-14: 하드코딩된 색상 대신 디자인 토큰/리소스로 치환 권장

디자인 시스템 색(예: Yellow10)을 이미 보강하셨다면, 벡터 내부의 색(#FFBF00)과 투명(#00000000)도 리소스/토큰을 사용하도록 바꾸는 편이 유지보수 및 테마 대응(다크모드, 동적 색상 등)에 유리합니다.

다음과 같이 치환을 제안드립니다(리소스 명은 예시이며, 실제 팔레트명에 맞춰 조정해 주세요):

-        android:fillColor="#FFBF00"
+        android:fillColor="@color/yellow10"
...
-        android:fillColor="#00000000"
+        android:fillColor="@android:color/transparent"
...
-        android:strokeColor="#FFBF00"
+        android:strokeColor="@color/yellow10"

1-15: 아이콘 테마 대응 고려 사항

해당 아이콘이 테마 색상에 따라 변해야 한다면, 경로별 색 고정 대신 View/Compose 레벨 tint 적용 전략을 검토해 주세요. 현재는 동일 pathData를 fill+stroke 두 번 선언해 스타일링하고 있는데, 경우에 따라선 단일 path + tint가 간결할 수 있습니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/component/template/EmptyRoutineListView.kt (4)

45-63: 접근성: 버튼 역할/터치 타깃 크기 보완 권장

현재 CTA는 커스텀 Box + clickable로 구현되어 있어 접근성 역할, 최소 터치 영역(48dp) 충족이 보장되지 않습니다. Role 지정과 최소 크기 확보를 권장합니다.

+import androidx.compose.foundation.layout.sizeIn
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.semantics
...
         Box(
             modifier = Modifier
                 .background(
                     color = BitnagilTheme.colors.coolGray96,
                     shape = RoundedCornerShape(8.dp),
                 )
                 .clickableWithoutRipple { onRegisterRoutineClick() }
+                .semantics { role = Role.Button }
+                .sizeIn(minWidth = 48.dp, minHeight = 48.dp)
                 .padding(
                     vertical = 10.dp,
                     horizontal = 14.dp,
                 ),
             contentAlignment = Alignment.Center,
         ) {

가능하다면 Material3의 Button 변형을 사용하는 것도 시맨틱/포커스/상태표현 면에서 일관성이 높습니다.


25-29: 센터 정렬 의도 확인(부모 컨테이너 의존 여부)

Column에 Center 정렬만 있고 사이즈 확장이 없어 부모 크기에 의존합니다. 상위에서 fillMaxSize로 감싸지 않는 경우 기대한 중앙 정렬이 되지 않을 수 있습니다. 의도에 따라 다음 중 하나를 고려해 주세요.

  • 이 컴포저블 자체가 전체 화면을 담당: modifier.fillMaxSize() 추가
  • 부모가 레이아웃을 잡고 이 뷰는 폭만 채움: modifier.fillMaxWidth() 추가

예시:

-    Column(
+    Column(
         verticalArrangement = Arrangement.Center,
         horizontalAlignment = Alignment.CenterHorizontally,
-        modifier = modifier,
+        modifier = modifier.fillMaxWidth(),
     ) {

30-35: 고정 높이 제거로 폰트 스케일 호환성 개선

Text에 고정 높이(28dp)를 주면 사용자 폰트 스케일/접근성 설정에서 잘릴 수 있습니다. 불필요한 고정 높이는 제거를 권장합니다.

-        Text(
-            text = stringResource(R.string.routine_empty_title),
-            style = BitnagilTheme.typography.subtitle1SemiBold,
-            color = BitnagilTheme.colors.coolGray30,
-            modifier = Modifier.height(28.dp),
-        )
+        Text(
+            text = stringResource(R.string.routine_empty_title),
+            style = BitnagilTheme.typography.subtitle1SemiBold,
+            color = BitnagilTheme.colors.coolGray30,
+        )

67-71: Preview에 테마 적용

BitnagilTheme에 의존하는 색/타이포를 사용하므로 Preview도 Theme로 감싸는 편이 안전합니다.

 @Preview
 @Composable
 private fun EmptyRoutineListViewPreview() {
-    EmptyRoutineListView(onRegisterRoutineClick = {})
+    BitnagilTheme {
+        EmptyRoutineListView(onRegisterRoutineClick = {})
+    }
 }
data/src/main/java/com/threegap/bitnagil/data/routine/datasource/RoutineRemoteDataSource.kt (1)

11-13: API 변경 반영 LGTM + 간단 KDoc 추가 제안

deleteRoutineForDay(routineId) 전환과 getRoutine 추가 방향성 좋습니다. 후속 유지보수를 위해 각 메서드의 서버 엔드포인트/의도(예: v2 day 삭제 vs v1 routine 조회)를 간단 KDoc으로 명시하면 가독성이 더 좋아집니다.

core/designsystem/src/main/res/drawable/ic_outside.xml (2)

7-15: 하드코딩 색상 정리 및 투명 색 상수화

  • 브랜드/테마 색(#0093F5) 하드코딩 대신 디자인 시스템 색 리소스로 추상화하면 테마 확장에 유리합니다.
  • 투명 색상은 @android:color/transparent 사용이 명시적입니다.

부분 치환 예시:

-        android:fillColor="#00000000"
+        android:fillColor="@android:color/transparent"
...
-        android:fillColor="#00000000"
+        android:fillColor="@android:color/transparent"

참고: 파란색/흰색도 가능하면 @color 리소스를 통해 관리하는 것을 권장드립니다.


1-33: 아이콘 틴트 전략 검토

해당 아이콘이 상황/테마에 따라 색이 바뀌어야 한다면 path별 고정 색상 대신 벡터 전체 tint를 활용하는 설계를 고려해 주세요. 디자인이 고정 원색을 요구하면 현 구조 유지도 무방합니다.

domain/src/main/java/com/threegap/bitnagil/domain/routine/model/Routine.kt (2)

17-32: 도메인 계층에서 UI 문자열 생성은 레이어 침투 — 포맷팅은 프리젠테이션으로 이동 권장

"하루종일"/"시간 미정"/"오전/오후 h:mm" 등 사용자 노출 문자열은 프리젠테이션 레이어에서 책임지는 것이 일반적입니다. 도메인이 Locale/문구를 알게 되면 테스트/국제화/재사용성이 떨어집니다.

대안:

  • 도메인: executionTime을 String 대신 LocalTime(또는 value class)으로 모델링하고, all-day 여부는 별 플래그로 제공.
  • 프리젠테이션: Locale에 맞는 포맷터로 화면용 문자열 생성(에러 시 “시간 미정” 같은 텍스트도 이 레이어에서 처리).

현재 구조를 유지하되, toUiModel()에서만 formattedExecutionTime을 사용하도록 범위를 최소화하는 것도 단계적 완화책입니다.


21-31: Detekt: SwallowedException 경고 해소 + 포맷터/Locale 정적화

  • 예외는 구체 타입(DateTimeParseException)만 처리하고, 포맷터/Locale은 companion object로 올려 재사용하면 성능/가독성이 좋아집니다. runCatching을 쓰면 SwallowedException 규칙도 피할 수 있습니다.
 import java.time.LocalTime
 import java.time.format.DateTimeFormatter
+import java.time.format.DateTimeParseException
 import java.util.Locale
...
-) {
-    private val koreanLocale = Locale.KOREAN
-    private val executionTimeFormatter12 = DateTimeFormatter.ofPattern("a h:mm", koreanLocale)
+) {
+    companion object {
+        private val KOREAN_LOCALE: Locale = Locale.KOREAN
+        private val EXECUTION_TIME_FORMATTER_12: DateTimeFormatter =
+            DateTimeFormatter.ofPattern("a h:mm", KOREAN_LOCALE)
+    }
 
-    val formattedExecutionTime: String =
-        try {
-            val time = LocalTime.parse(executionTime)
-            if (time == LocalTime.MIDNIGHT) {
-                "하루종일"
-            } else {
-                time.format(executionTimeFormatter12)
-            }
-        } catch (e: Exception) {
-            "시간 미정"
-        }
+    val formattedExecutionTime: String =
+        runCatching {
+            val time = LocalTime.parse(executionTime)
+            if (time == LocalTime.MIDNIGHT) "하루종일"
+            else time.format(EXECUTION_TIME_FORMATTER_12)
+        }.getOrElse { throwable ->
+            if (throwable is DateTimeParseException) "시간 미정" else "시간 미정"
+        }
 }

추가로, executionTime이 백엔드에서 “HH:mm” 외 포맷으로 올 수 있다면, 파서/검증을 data 계층 변환 시점으로 이전하는 것도 고려해 주세요.

core/designsystem/src/main/res/drawable/ic_rest.xml (2)

6-15: 동일 pathData를 fill/stroke로 이중 렌더링 — 파일 크기/렌더링 오버헤드 발생

첫 번째 는 동일한 pathData를 채움으로, 두 번째 는 선으로 다시 그려 중복 렌더링하고 있습니다. Android VectorDrawable은 하나의 path에 fillColor와 stroke 속성을 모두 지정할 수 있으므로, 두 경로를 하나로 합치면 사이즈와 렌더 비용을 줄일 수 있습니다.

변경 예시(컨셉):

  • 첫 번째 에 stroke 속성 추가
  • 두 번째 제거

변경 후 시각적 차이가 없는지 디자인 확인을 부탁드립니다.


7-7: 하드코딩된 색상(#49CA00, #FFFFFF) → 테마/팔레트 자원 참조 권장

디자인 시스템 아이콘에서 고정 HEX 색상을 직접 사용하면 다크 모드/테마 전환 시 일관성이 깨질 수 있습니다. 가능하다면 Color 리소스(@color/...)나 디자인 시스템 팔레트(BitnagilTheme.colors.*)에 매핑된 리소스를 사용해 주세요.

Also applies to: 20-21, 27-27

presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/component/template/WeeklyDatePicker.kt (3)

46-46: LocalDate.now() 반복 호출 최소화

루프 내에서 LocalDate.now()를 매 항목마다 호출하고 있습니다. 동일한 컴포지션 프레임 내에서는 한 번만 계산해 재사용하는 편이 깔끔합니다.

아래와 같이 hoist해 주세요:

) {
-    Row(
+    val today = LocalDate.now()
+    Row(
         horizontalArrangement = Arrangement.SpaceBetween,
         modifier = modifier
             .fillMaxWidth(),
     ) {
         weeklyDates.forEach { date ->
             DateItem(
                 date = date,
                 isSelected = selectedDate == date,
-                isToday = date == LocalDate.now(),
+                isToday = date == today,
                 onDateClick = { onDateSelect(date) },
                 modifier = Modifier.padding(bottom = 18.dp),
             )

Also applies to: 37-41


62-71: 접근성(선택 상태/역할/터치 타겟) 메타데이터 보강 권장

  • clickableWithoutRipple만으로는 선택 상태가 보조기기에 충분히 전달되지 않을 수 있습니다. selectable(semantics 제공) 사용을 권장합니다.
  • 선택 가능한 탭/날짜 역할(Role.Tab 등) 지정, selected 상태 제공을 권장합니다.
  • 터치 타겟 최소 48dp 확보(outer Column에 minimumTouchTargetSize 또는 padding)도 고려해 주세요.

예시:

// import androidx.compose.foundation.selection.selectable
// import androidx.compose.ui.semantics.Role

Column(
  verticalArrangement = Arrangement.spacedBy(7.dp),
  horizontalAlignment = Alignment.CenterHorizontally),
  modifier = modifier
    .selectable(
      selected = isSelected,
      onClick = onDateClick,
      role = Role.Tab,
    )
    // .minimumTouchTargetSize() // 필요 시 적용
) { ... }

테스트 자동화 용이성을 위해 TestTag 부여도 고려해 주세요.

Also applies to: 73-87


68-71: 문자열 하드코딩("오늘") → 문자열 리소스 사용 권장

국제화/지역화 및 일관성 유지를 위해 문자열 리소스로 분리하는 것이 좋습니다. 예: string.today 등.

domain/src/main/java/com/threegap/bitnagil/domain/routine/model/RecommendedRoutineType.kt (1)

14-16: 서버 문자열 매핑의 대소문자/미세 포맷 차이 허용

현재 it.name == categoryName은 대소문자에 민감합니다. 서버 스펙 변경 여지를 고려해 case-insensitive 매칭 정도는 허용하는 편이 안전합니다.

아래처럼 느슨한 매칭으로 보완할 수 있습니다:

-        fun fromString(categoryName: String?): RecommendedRoutineType? =
-            RecommendedRoutineType.entries.find { it.name == categoryName }
+        fun fromString(categoryName: String?): RecommendedRoutineType? =
+            categoryName?.let { name ->
+                RecommendedRoutineType.entries.firstOrNull { it.name.equals(name, ignoreCase = true) }
+            }

필요 시 공백/하이픈 정규화(예: replace("-", "_"))도 고려해 주세요.

core/designsystem/src/main/res/drawable/ic_trash.xml (1)

6-15: 동일 pathData를 fill+stroke로 중복 렌더링 — 불필요한 오버드로우

첫 번째 path(채움)와 두 번째 path(동일 pathData의 스트로크)가 동일 경로를 두 번 그립니다. 일관된 스트로크 기반 아이콘 스타일을 지향한다면 첫 번째 path 제거 또는 병합을 권장합니다.

채움 제거 방향 예시:

-    <path
-        android:fillColor="#989BA2"
-        android:pathData="M13.334,5.033C13,5.033 ... L13.334,5.033Z" />

또는 첫 path에 stroke 속성을 추가하고 두 번째 path를 제거하는 방식도 가능합니다. 변경 후 아이콘 외형이 의도와 동일한지 시각적 검증 부탁드립니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/home/HomeViewModel.kt (1)

123-127: reduceState 안에서 container.stateFlow.value 접근 대신 전달된 state 사용

동일 스코프의 최신 스냅샷이 보장되는 state 파라미터가 존재하므로, container.stateFlow.value를 다시 읽기보다는 state를 쓰는 편이 안전하고 일관적입니다.

아래처럼 교체해 주세요:

-                val selectedDate = container.stateFlow.value.selectedDate.toString()
+                val selectedDate = state.selectedDate.toString()
                 sendSideEffect(HomeSideEffect.NavigateToRoutineList(selectedDate))
                 null
app/src/main/java/com/threegap/bitnagil/Route.kt (1)

45-46: selectedDate는 형식(예: yyyy-MM-dd) 명시/검증이 필요합니다

문자열로 날짜를 받으면 로케일/포맷 불일치로 회귀가 자주 발생합니다. 최소한 예상 포맷을 KDoc에 명시하거나, 가능하면 타입 안전(예: LocalDate + 커스텀 직렬화)으로 교체를 권장합니다.

다음과 같이 포맷을 명시하는 KDoc을 추가하는 정도만으로도 사용처 혼선을 줄일 수 있습니다:

@@
-    @Serializable
-    data class RoutineList(val selectedDate: String) : Route
+    /**
+     * 루틴 리스트 화면으로 이동 시 선택된 날짜.
+     * 형식: ISO-8601(yyyy-MM-dd), 시간대 비의존. 서버/클라이언트 간 동일 포맷 사용.
+     */
+    @Serializable
+    data class RoutineList(val selectedDate: String) : Route

추가 확인:

  • Home → RoutineList 네비게이션 파이프라인 전 구간에서 selectedDate를 동일 포맷으로 생성/소비하는지 확인 부탁드립니다(예: DateTimeFormatter.ISO_LOCAL_DATE).
core/designsystem/src/main/res/drawable/ic_grow.xml (1)

16-22: 중복 path를 하나로 합칠 수 있고, 하드코딩 색상 대신 테마/틴트 적용을 고려해 주세요

두 번째/세 번째 path는 동일한 pathData로 fill과 stroke만 분리되어 있어 한 path로 통합이 가능합니다. 또한 아이콘 색상을 XML에 고정(#F55174)하면 테마/다크모드 대응과 전역 토큰 치환이 어려워집니다. 가능하면 단색(예: #000)으로 제작 후 Compose의 tint로 착색하거나, @color 리소스를 참조하도록 하는 방식을 권장합니다.

중복 path 통합 예시(동일 기능 유지):

@@
-    <path
-        android:fillColor="#F55174"
-        android:pathData="M4.2,15.726C4.42,15.546 4.43,15.486 4.65,15.356C4.89,15.206 5.23,15.306 5.47,15.196C5.73,15.076 5.91,14.886 6.17,14.816C6.44,14.746 6.67,14.576 6.93,14.546C7.19,14.516 7.46,14.756 7.73,14.766C8,14.776 8.29,14.496 8.56,14.546C8.83,14.596 9.13,14.546 9.39,14.616C9.65,14.686 9.96,14.676 10.21,14.766C10.46,14.856 10.559,15.356 10.819,15.456C11.08,15.556 11.399,15.476 11.649,15.586C11.899,15.696 12.059,15.996 12.309,16.106C12.559,16.216 12.969,15.946 13.219,16.056C13.469,16.166 13.63,16.476 13.889,16.566C14.149,16.656 14.34,16.976 14.599,17.046C14.859,17.116 15.21,16.806 15.479,16.846C15.75,16.886 15.979,17.256 16.25,17.266C16.52,17.276 16.76,16.856 17.039,16.826C17.319,16.796 17.61,17.046 17.879,16.966C18.129,16.896 18.399,16.816 18.649,16.696C18.889,16.586 19.129,16.466 19.369,16.316C19.59,16.176 19.549,15.926 19.779,15.756C19.869,15.366 20.039,15.406 20.039,14.996C20.039,14.586 19.889,14.596 19.889,14.186C19.889,13.776 20.039,13.786 20.039,13.376C20.039,12.966 20.039,12.966 20.039,12.566C20.039,12.166 20.17,12.156 20.17,11.756C20.17,11.356 19.99,11.346 19.99,10.946C19.99,10.546 19.76,10.536 19.76,10.136C19.76,9.736 20.219,9.726 20.219,9.326C20.219,8.926 20.27,8.916 20.27,8.516C20.27,8.116 19.94,8.106 19.94,7.706C19.94,7.306 20.02,7.296 20.02,6.896C20.02,6.496 19.779,6.486 19.779,6.076C19.779,5.666 20.219,5.666 20.219,5.256C20.219,4.846 20.379,4.736 20.25,4.356C20.029,4.536 19.68,4.966 19.459,5.106C19.219,5.256 18.91,5.306 18.68,5.416C18.42,5.536 18.129,5.566 17.879,5.636C17.61,5.706 17.33,5.686 17.059,5.716C16.789,5.746 16.51,5.906 16.24,5.896C15.969,5.886 15.67,5.926 15.399,5.876C15.13,5.826 14.88,5.656 14.62,5.586C14.359,5.516 14.179,5.186 13.929,5.086C13.679,4.986 13.38,5.016 13.13,4.916C12.88,4.816 12.59,4.806 12.34,4.696C12.09,4.586 11.88,4.396 11.63,4.286C11.38,4.176 11.12,4.096 10.87,3.996C10.62,3.896 10.29,4.006 10.04,3.916C9.79,3.826 9.56,3.696 9.3,3.616C9.04,3.536 8.8,3.476 8.53,3.426C8.26,3.376 8.04,2.896 7.77,2.886C7.5,2.876 7.2,2.956 6.93,2.986C6.66,3.016 6.41,3.216 6.15,3.286C5.9,3.356 5.59,3.336 5.33,3.456C5.09,3.566 4.94,3.866 4.7,4.006C4.48,4.146 4.39,4.296 4.17,4.466C4.08,4.856 3.98,4.836 3.98,5.236C3.98,5.636 3.79,5.636 3.79,6.046C3.79,6.456 4.06,6.446 4.06,6.856C4.06,7.266 4.27,7.266 4.27,7.666C4.27,8.066 3.89,8.076 3.89,8.476C3.89,8.876 3.87,8.886 3.87,9.286C3.87,9.686 3.96,9.696 3.96,10.096C3.96,10.496 4.1,10.506 4.1,10.906C4.1,11.306 4.29,11.316 4.29,11.716C4.29,12.116 4.16,12.126 4.16,12.526C4.16,12.926 3.78,12.936 3.78,13.336C3.78,13.736 4.19,13.746 4.19,14.156C4.19,14.566 4.04,14.566 4.07,14.966C4.1,15.356 4.1,15.326 4.24,15.706L4.2,15.726Z" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M4.2,15.726C4.42,15.546 4.43,15.486 4.65,15.356C4.89,15.206 5.23,15.306 5.47,15.196C5.73,15.076 5.91,14.886 6.17,14.816C6.44,14.746 6.67,14.576 6.93,14.546C7.19,14.516 7.46,14.756 7.73,14.766C8,14.776 8.29,14.496 8.56,14.546C8.83,14.596 9.13,14.546 9.39,14.616C9.65,14.686 9.96,14.676 10.21,14.766C10.46,14.856 10.559,15.356 10.819,15.456C11.08,15.556 11.399,15.476 11.649,15.586C11.899,15.696 12.059,15.996 12.309,16.106C12.559,16.216 12.969,15.946 13.219,16.056C13.469,16.166 13.63,16.476 13.889,16.566C14.149,16.656 14.34,16.976 14.599,17.046C14.859,17.116 15.21,16.806 15.479,16.846C15.75,16.886 15.979,17.256 16.25,17.266C16.52,17.276 16.76,16.856 17.039,16.826C17.319,16.796 17.61,17.046 17.879,16.966C18.129,16.896 18.399,16.816 18.649,16.696C18.889,16.586 19.129,16.466 19.369,16.316C19.59,16.176 19.549,15.926 19.779,15.756C19.869,15.366 20.039,15.406 20.039,14.996C20.039,14.586 19.889,14.596 19.889,14.186C19.889,13.776 20.039,13.786 20.039,13.376C20.039,12.966 20.039,12.966 20.039,12.566C20.039,12.166 20.17,12.156 20.17,11.756C20.17,11.356 19.99,11.346 19.99,10.946C19.99,10.546 19.76,10.536 19.76,10.136C19.76,9.736 20.219,9.726 20.219,9.326C20.219,8.926 20.27,8.916 20.27,8.516C20.27,8.116 19.94,8.106 19.94,7.706C19.94,7.306 20.02,7.296 20.02,6.896C20.02,6.496 19.779,6.486 19.779,6.076C19.779,5.666 20.219,5.666 20.219,5.256C20.219,4.846 20.379,4.736 20.25,4.356C20.029,4.536 19.68,4.966 19.459,5.106C19.219,5.256 18.91,5.306 18.68,5.416C18.42,5.536 18.129,5.566 17.879,5.636C17.61,5.706 17.33,5.686 17.059,5.716C16.789,5.746 16.51,5.906 16.24,5.896C15.969,5.886 15.67,5.926 15.399,5.876C15.13,5.826 14.88,5.656 14.62,5.586C14.359,5.516 14.179,5.186 13.929,5.086C13.679,4.986 13.38,5.016 13.13,4.916C12.88,4.816 12.59,4.806 12.34,4.696C12.09,4.586 11.88,4.396 11.63,4.286C11.38,4.176 11.12,4.096 10.87,3.996C10.62,3.896 10.29,4.006 10.04,3.916C9.79,3.826 9.56,3.696 9.3,3.616C9.04,3.536 8.8,3.476 8.53,3.426C8.26,3.376 8.04,2.896 7.77,2.886C7.5,2.876 7.2,2.956 6.93,2.986C6.66,3.016 6.41,3.216 6.15,3.286C5.9,3.356 5.59,3.336 5.33,3.456C5.09,3.566 4.94,3.866 4.7,4.006C4.48,4.146 4.39,4.296 4.17,4.466C4.08,4.856 3.98,4.836 3.98,5.236C3.98,5.636 3.79,5.636 3.79,6.046C3.79,6.456 4.06,6.446 4.06,6.856C4.06,7.266 4.27,7.266 4.27,7.666C4.27,8.066 3.89,8.076 3.89,8.476C3.89,8.876 3.87,8.886 3.87,9.286C3.87,9.686 3.96,9.696 3.96,10.096C3.96,10.496 4.1,10.506 4.1,10.906C4.1,11.306 4.29,11.316 4.29,11.716C4.29,12.116 4.16,12.126 4.16,12.526C4.16,12.926 3.78,12.936 3.78,13.336C3.78,13.736 4.19,13.746 4.19,14.156C4.19,14.566 4.04,14.566 4.07,14.966C4.1,15.356 4.1,15.326 4.24,15.706L4.2,15.726Z"
-        android:strokeWidth="2"
-        android:strokeColor="#F55174"
-        android:strokeLineCap="round"
-        android:strokeLineJoin="round" />
+    <path
+        android:fillColor="#F55174"
+        android:strokeWidth="2"
+        android:strokeColor="#F55174"
+        android:strokeLineCap="round"
+        android:strokeLineJoin="round"
+        android:pathData="M4.2,15.726C4.42,15.546 4.43,15.486 4.65,15.356C4.89,15.206 5.23,15.306 5.47,15.196C5.73,15.076 5.91,14.886 6.17,14.816C6.44,14.746 6.67,14.576 6.93,14.546C7.19,14.516 7.46,14.756 7.73,14.766C8,14.776 8.29,14.496 8.56,14.546C8.83,14.596 9.13,14.546 9.39,14.616C9.65,14.686 9.96,14.676 10.21,14.766C10.46,14.856 10.559,15.356 10.819,15.456C11.08,15.556 11.399,15.476 11.649,15.586C11.899,15.696 12.059,15.996 12.309,16.106C12.559,16.216 12.969,15.946 13.219,16.056C13.469,16.166 13.63,16.476 13.889,16.566C14.149,16.656 14.34,16.976 14.599,17.046C14.859,17.116 15.21,16.806 15.479,16.846C15.75,16.886 15.979,17.256 16.25,17.266C16.52,17.276 16.76,16.856 17.039,16.826C17.319,16.796 17.61,17.046 17.879,16.966C18.129,16.896 18.399,16.816 18.649,16.696C18.889,16.586 19.129,16.466 19.369,16.316C19.59,16.176 19.549,15.926 19.779,15.756C19.869,15.366 20.039,15.406 20.039,14.996C20.039,14.586 19.889,14.596 19.889,14.186C19.889,13.776 20.039,13.786 20.039,13.376C20.039,12.966 20.039,12.966 20.039,12.566C20.039,12.166 20.17,12.156 20.17,11.756C20.17,11.356 19.99,11.346 19.99,10.946C19.99,10.546 19.76,10.536 19.76,10.136C19.76,9.736 20.219,9.726 20.219,9.326C20.219,8.926 20.27,8.916 20.27,8.516C20.27,8.116 19.94,8.106 19.94,7.706C19.94,7.306 20.02,7.296 20.02,6.896C20.02,6.496 19.779,6.486 19.779,6.076C19.779,5.666 20.219,5.666 20.219,5.256C20.219,4.846 20.379,4.736 20.25,4.356C20.029,4.536 19.68,4.966 19.459,5.106C19.219,5.256 18.91,5.306 18.68,5.416C18.42,5.536 18.129,5.566 17.879,5.636C17.61,5.706 17.33,5.686 17.059,5.716C16.789,5.746 16.51,5.906 16.24,5.896C15.969,5.886 15.67,5.926 15.399,5.876C15.13,5.826 14.88,5.656 14.62,5.586C14.359,5.516 14.179,5.186 13.929,5.086C13.679,4.986 13.38,5.016 13.13,4.916C12.88,4.816 12.59,4.806 12.34,4.696C12.09,4.586 11.88,4.396 11.63,4.286C11.38,4.176 11.12,4.096 10.87,3.996C10.62,3.896 10.29,4.006 10.04,3.916C9.79,3.826 9.56,3.696 9.3,3.616C9.04,3.536 8.8,3.476 8.53,3.426C8.26,3.376 8.04,2.896 7.77,2.886C7.5,2.876 7.2,2.956 6.93,2.986C6.66,3.016 6.41,3.216 6.15,3.286C5.9,3.356 5.59,3.336 5.33,3.456C5.09,3.566 4.94,3.866 4.7,4.006C4.48,4.146 4.39,4.296 4.17,4.466C4.08,4.856 3.98,4.836 3.98,5.236C3.98,5.636 3.79,5.636 3.79,6.046C3.79,6.456 4.06,6.446 4.06,6.856C4.06,7.266 4.27,7.266 4.27,7.666C4.27,8.066 3.89,8.076 3.89,8.476C3.89,8.876 3.87,8.886 3.87,9.286C3.87,9.686 3.96,9.696 3.96,10.096C3.96,10.496 4.1,10.506 4.1,10.906C4.1,11.306 4.29,11.316 4.29,11.716C4.29,12.116 4.16,12.126 4.16,12.526C4.16,12.926 3.78,12.936 3.78,13.336C3.78,13.736 4.19,13.746 4.19,14.156C4.19,14.566 4.04,14.566 4.07,14.966C4.1,15.356 4.1,15.326 4.24,15.706L4.2,15.726Z" />

추가 고려:

  • 색상은 @color 리소스(디자인 토큰) 참조로 전환하거나, 검정 단색 후 Compose에서 tint로 주입하면 유지보수가 쉬워집니다.
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilTextButton.kt (1)

127-135: delete 프리셋의 pressed/disabled 시각적 피드백 강화 제안

현재 pressed/disabled가 default와 동일(또는 유사)해 상호작용 피드백이 약합니다. 파괴적 액션은 누름/비활성 상태를 더 명확히 구분하는 것이 접근성과 사용성에 좋습니다.

예시(누름: 더 진하거나 다른 레드, 비활성: 회색계로 전환):

 @Composable
 fun delete(): BitnagilTextButtonColor = BitnagilTextButtonColor(
-    defaultBackgroundColor = BitnagilTheme.colors.error10,
-    pressedBackgroundColor = BitnagilTheme.colors.error10,
-    disabledBackgroundColor = BitnagilTheme.colors.error10,
-    defaultTextColor = BitnagilTheme.colors.white,
-    pressedTextColor = BitnagilTheme.colors.white,
-    disabledTextColor = BitnagilTheme.colors.white,
+    defaultBackgroundColor = BitnagilTheme.colors.error10,
+    // 누름 시 더 강한 레드로 대비 강화
+    pressedBackgroundColor = BitnagilTheme.colors.error,
+    // 비활성은 명확히 비활성처럼 보이도록 중성색 사용
+    disabledBackgroundColor = BitnagilTheme.colors.coolGray96,
+    defaultTextColor = BitnagilTheme.colors.white,
+    pressedTextColor = BitnagilTheme.colors.white,
+    disabledTextColor = BitnagilTheme.colors.coolGray40,
 )

추가로 Preview에 delete 예시를 하나 추가하면 리뷰/QA에 도움이 됩니다(참고용 코드):

@Preview
@Composable
private fun BitnagilTextButtonDeletePreview() {
    Column(Modifier.padding(16.dp)) {
        BitnagilTextButton(
            text = "삭제하기",
            onClick = {},
            colors = BitnagilTextButtonColor.delete(),
            modifier = Modifier.fillMaxWidth(),
        )
        Spacer(modifier = Modifier.height(12.dp))
        BitnagilTextButton(
            text = "삭제(비활성)",
            onClick = {},
            enabled = false,
            colors = BitnagilTextButtonColor.delete(),
            modifier = Modifier.fillMaxWidth(),
        )
    }
}

원하시면 버튼 눌림에 대한 리플 효과(indication)를 되살리는 방안도 제안드릴 수 있습니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/model/DayRoutinesUiModel.kt (1)

8-10: 필드명 일관성(루틴 리스트 vs 루틴 맵) 검토 제안

동일 레이어의 RoutinesUiModel가 routines(맵)이고, 일자 단위는 routineList(리스트)로 네이밍이 달라 혼동 여지가 약간 있습니다. 팀 규칙에 맞춰 routines/routinesByDate/routinesOfDay 등으로 네이밍 일관성을 맞추는 것을 고려해주세요.

- data class DayRoutinesUiModel(
-     val routineList: List<RoutineUiModel> = emptyList(),
- ) : Parcelable
+ data class DayRoutinesUiModel(
+     val routines: List<RoutineUiModel> = emptyList(),
+ ) : Parcelable

(참고: 변경 시 연쇄 수정 범위가 넓을 수 있으므로 후속 PR 검토 권장)

presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/model/RoutineListSideEffect.kt (1)

7-7: 네비게이션 파라미터 기본값 제공 제안

nullable 파라미터는 기본값을 두면 호출부 가독성이 좋아집니다.

-    data class NavigateToWriteRoutine(val routineId: String?, val applyDate: String?) : RoutineListSideEffect
+    data class NavigateToWriteRoutine(
+        val routineId: String? = null,
+        val applyDate: String? = null,
+    ) : RoutineListSideEffect
presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/model/RoutinesUiModel.kt (1)

8-10: 동일 명칭 UI 모델 충돌 가능성 주의
현재 presentation.routinelist.model.RoutinesUiModel은 아직 다른 파일에서 import되지 않았으나, presentation.home.model.RoutinesUiModel은 아래 파일들에서 사용 중입니다. 두 모델을 동시에 import해야 하는 경우 혼동을 피하기 위해 import alias를 사용하거나, 이후 PR에서 네이밍을 조정하는 것을 권장드립니다.

확인된 사용 위치:

  • presentation/src/main/java/com/threegap/bitnagil/presentation/home/HomeViewModel.kt
    import com.threegap.bitnagil.presentation.home.model.RoutinesUiModel
  • presentation/src/main/java/com/threegap/bitnagil/presentation/home/component/template/WeeklyDatePicker.kt
    import com.threegap.bitnagil.presentation.home.model.RoutinesUiModel

예시: import alias 적용

import com.threegap.bitnagil.presentation.home.model.RoutinesUiModel as HomeRoutinesUiModel
import com.threegap.bitnagil.presentation.routinelist.model.RoutinesUiModel as RoutineListRoutinesUiModel
domain/src/main/java/com/threegap/bitnagil/domain/routine/repository/RoutineRepository.kt (1)

11-12: 삭제 책임 구분이 모호합니다 — KDoc/네이밍으로 의도 명확화 제안

deleteRoutine(routineId)(전체 삭제)와 deleteRoutineForDay(routineId)(특정 일자 삭제)의 차이가 네이밍만으로는 바로 드러나지 않습니다. 실사용 시 오사용 가능성이 있어, 다음 보완을 권장합니다:

  • 각 메서드에 KDoc으로 동작과 전제조건(반복 루틴/단일 루틴의 처리 차이 등)을 명시
  • 가능하다면 deleteRoutineCompletely vs deleteRoutineForDay 등으로 네이밍을 더 구체화

큰 변경이 아니므로 도메인 계층에서의 의도 전달력이 좋아집니다.

data/src/main/java/com/threegap/bitnagil/data/routine/datasourceImpl/RoutineRemoteDataSourceImpl.kt (1)

35-38: safeUnitApiCall은 비즈니스 실패코드 검증이 없습니다

safeUnitApiCall은 HTTP 예외만 예외 처리하고, 200 OK이면서 비즈니스 레벨 실패 코드(예: code != SUCCESS)는 성공으로 처리합니다. 서버가 성공/실패를 HTTP가 아닌 본문 코드로 표현한다면, 실패를 성공으로 오인할 수 있습니다. 공통 로직 개선을 제안합니다.

가능한 개선 방향(예시, 공통 유틸 수정 필요):

// SafeApiCall.kt (예시)
internal suspend inline fun <T> safeUnitApiCallChecked(
    crossinline apiCall: suspend () -> BaseResponse<T>,
): Result<Unit> =
    try {
        val response = apiCall()
        // TODO: 프로젝트의 성공 기준(예: response.code == "SUCCESS" 또는 response.success == true)에 맞춰 검사
        val isSuccess = /* check response */
        if (isSuccess) Result.success(Unit)
        else Result.failure(BitnagilError(code = "BUSINESS_FAIL:${response.code}", message = response.message))
    } catch (e: Exception) {
        handleApiException(e)
    }

사용처:

-override suspend fun deleteRoutineForDay(routineId: String): Result<Unit> =
-    safeUnitApiCall {
-        routineService.deleteRoutineForDay(routineId)
-    }
+override suspend fun deleteRoutineForDay(routineId: String): Result<Unit> =
+    safeUnitApiCallChecked {
+        routineService.deleteRoutineForDay(routineId)
+    }
domain/src/main/java/com/threegap/bitnagil/domain/routine/usecase/DeleteRoutineForDayUseCase.kt (1)

6-8: 간단한 KDoc 추가 제안

해당 유스케이스가 “특정 일자 삭제”임을 KDoc으로 명시하면 ViewModel/호출부에서 혼선을 줄일 수 있습니다(전체 삭제 유스케이스와 대비).

presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/RoutineListScreen.kt (2)

47-52: 네비게이션 TODO: 라우팅 파라미터 정의와 컨테이너 인자 설계 제안

NavigateToWriteRoutine 처리가 주석 상태입니다. 실제 연결 시 다음과 같이 컨테이너 인자로 콜백을 주입하는 형태가 깔끔합니다(필요 시 선택 파라미터로 루틴 ID/적용일 전달).

예시(컨테이너 시그니처/호출부 수정 필요):

- fun RoutineListScreenContainer(
-     navigateToBack: () -> Unit,
-     viewModel: RoutineListViewModel = hiltViewModel(),
- ) {
+ fun RoutineListScreenContainer(
+     navigateToBack: () -> Unit,
+     navigateToWriteRoutine: (routineId: String?, applyDate: String) -> Unit,
+     viewModel: RoutineListViewModel = hiltViewModel(),
+ ) {
...
-            is RoutineListSideEffect.NavigateToWriteRoutine -> {
-//                TODO: 네비게이션 연결하기
-//                navigateToWriteRoutine(
-//                    routineId = sideEffect.routineId,
-//                    applyDate = sideEffect.applyDate,
-//                )
-            }
+            is RoutineListSideEffect.NavigateToWriteRoutine ->
+                navigateToWriteRoutine(
+                    routineId = sideEffect.routineId,
+                    applyDate = sideEffect.applyDate,
+                )

MainNavHost에서 해당 콜백을 연결하면 완성됩니다.


111-115: 하드코딩된 타이틀 문자열을 리소스로 이전하세요

i18n/카피 일관성을 위해 string 리소스로 관리하는 것을 권장합니다. 아래처럼 교체 가능합니다.

다음 diff를 적용:

-        BitnagilTopBar(
-            title = "루틴리스트",
+        BitnagilTopBar(
+            title = stringResource(R.string.routine_list_title),
             showBackButton = true,
             onBackClick = onBackClick,
         )

추가로 필요한 변경(파일 외부):

  • import 추가
    import androidx.compose.ui.res.stringResource
  • res/values/strings.xml
    <string name="routine_list_title">루틴 리스트</string>
presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/component/template/EditConfirmBottomSheet.kt (1)

21-65: 코드 중복을 제거하여 유지보수성을 개선할 수 있습니다.

onApplyTodayonApplyTomorrow 콜백에서 동일한 코루틴 로직이 중복되고 있습니다. 공통 함수로 추출하여 중복을 제거하는 것을 고려해보세요.

다음과 같이 리팩토링할 수 있습니다:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun EditConfirmBottomSheet(
    onDismissRequest: () -> Unit,
    onApplyToday: () -> Unit,
    onApplyTomorrow: () -> Unit,
    modifier: Modifier = Modifier,
) {
    val sheetState = rememberModalBottomSheetState()
    val coroutineScope = rememberCoroutineScope()

+    fun handleAction(action: () -> Unit) {
+        action()
+        coroutineScope
+            .launch { sheetState.hide() }
+            .invokeOnCompletion {
+                if (!sheetState.isVisible) {
+                    onDismissRequest()
+                }
+            }
+    }

    ModalBottomSheet(
        sheetState = sheetState,
        onDismissRequest = onDismissRequest,
        contentColor = BitnagilTheme.colors.white,
        containerColor = BitnagilTheme.colors.white,
        modifier = modifier,
    ) {
        EditConfirmContent(
-            onApplyToday = {
-                onApplyToday()
-                coroutineScope
-                    .launch { sheetState.hide() }
-                    .invokeOnCompletion {
-                        if (!sheetState.isVisible) {
-                            onDismissRequest()
-                        }
-                    }
-            },
-            onApplyTomorrow = {
-                onApplyTomorrow()
-                coroutineScope
-                    .launch { sheetState.hide() }
-                    .invokeOnCompletion {
-                        if (!sheetState.isVisible) {
-                            onDismissRequest()
-                        }
-                    }
-            },
+            onApplyToday = { handleAction(onApplyToday) },
+            onApplyTomorrow = { handleAction(onApplyTomorrow) },
            modifier = Modifier
                .padding(horizontal = 24.dp)
                .padding(bottom = 26.dp),
        )
    }
}
presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/model/RoutineListState.kt (1)

17-18: 확장 함수 위치 재검토 필요

getCurrentWeekDays()
com.threegap.bitnagil.presentation.home.util.LocalDateExtension.kt에 정의되어 있으며
presentation.homepresentation.routinelist 영역에서 광범위하게 참조되고 있습니다.
해당 기능이 범용적이라면 더 공통적인 패키지로 이동하는 것을 권장드립니다.

  • 정의 위치
    • presentation/src/main/java/com/threegap/bitnagil/presentation/home/util/LocalDateExtension.kt
  • 주요 참조 위치
    • presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/model/RoutineListState.kt
    • presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/component/template/WeeklyDatePicker.kt
    • presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/RoutineListViewModel.kt
    • presentation/src/main/java/com/threegap/bitnagil/presentation/home/HomeViewModel.kt (2곳)
    • presentation/src/main/java/com/threegap/bitnagil/presentation/home/model/HomeState.kt
    • presentation/src/main/java/com/threegap/bitnagil/presentation/home/component/template/WeeklyDatePicker.kt

제안

  1. presentation/common/util 또는 core/util 등 적절한 공통 패키지 생성
  2. LocalDateExtension.kt 파일을 새 공통 패키지로 이동
  3. 각 사용처의 import 경로를 업데이트하여 의존성 정리
presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/component/template/RoutineDetailsCard.kt (4)

50-59: 헤더 아이콘 가독성(콘트라스트) 개선 제안

배경 컬러 위에 원본 벡터 컬러(틴트 null)를 그대로 쓰면 다크한 아이콘의 경우 대비가 떨어질 수 있습니다. 헤더 배지 내부 아이콘은 화이트 틴트를 권장합니다.

다음과 같이 변경을 고려해주세요:

 BitnagilIcon(
     id = if (routine.recommendedRoutineType != null) routine.recommendedRoutineType.getIcon() else R.drawable.ic_shine,
-    tint = null,
+    tint = BitnagilTheme.colors.white,
     modifier = Modifier
         .background(
             color = if (routine.recommendedRoutineType != null) routine.recommendedRoutineType.getColor() else BitnagilTheme.colors.yellow10,
             shape = RoundedCornerShape(4.dp),
         )
         .padding(4.dp),
 )

70-75: 아이콘 버튼 접근성(스크린리더) 라벨 추가

액션 버튼(편집/삭제)에 contentDescription이 없어 보조기기 사용 시 의미 전달이 어렵습니다. semantics 라벨을 부여해주세요.

 BitnagilIconButton(
     id = R.drawable.ic_edit,
     onClick = onEditClick,
+    modifier = Modifier.semantics { contentDescription = "편집" },
     tint = null,
     paddingValues = PaddingValues(12.dp),
 )
 
 BitnagilIconButton(
     id = R.drawable.ic_trash,
     onClick = onDeleteClick,
+    modifier = Modifier.semantics { contentDescription = "삭제" },
     tint = null,
     paddingValues = PaddingValues(12.dp),
 )

추가로 아래 import를 파일 상단에 추가해주세요:

import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics

Also applies to: 77-82


129-133: ‘기간’ 항목이 비어 보이는 UI를 완화

서버 API 미구현으로 기간 표시가 비어 있는 상태입니다. 사용자 혼란을 줄이기 위해 임시 플레이스홀더를 표시하거나 항목 자체를 숨기는 것을 권장합니다.

간단한 플레이스홀더 예:

-Text(
-    text = "기간: ",
+Text(
+    text = "기간: -",
     color = BitnagilTheme.colors.coolGray40,
     style = BitnagilTheme.typography.body2Medium,
 )

혹은 기간 데이터가 준비될 때까지 이 Text 블록을 조건부로 렌더링하지 않도록 처리해도 됩니다.


97-101: 하드코딩된 문자열의 리소스화 권장

"세부 루틴", "반복", "기간", "시간" 등 UI 문자열이 하드코딩되어 있습니다. 다국어 대응과 일관성 유지를 위해 string 리소스로 이전하는 것을 권장합니다.

Also applies to: 124-139

presentation/src/main/java/com/threegap/bitnagil/presentation/home/HomeScreen.kt (2)

97-98: 불필요한 이중 괄호 제거 제안

sendIntent((HomeIntent.OnShowMoreRoutinesClick))는 바깥 괄호가 불필요합니다. 간단히 정리하면 가독성이 좋아집니다.

-            viewModel.sendIntent((HomeIntent.OnShowMoreRoutinesClick))
+            viewModel.sendIntent(HomeIntent.OnShowMoreRoutinesClick)

160-172: 하드코딩 문자열 리소스화 권장

"루틴 리스트", "더보기" 텍스트는 string 리소스로 분리해 주세요. 디자인 시스템/다국어 정책과도 일치합니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/RoutineListViewModel.kt (1)

84-108: 적용(오늘/내일) 네비게이션 시 편집 확인 바텀시트 닫기

적용 액션 후에도 editConfirmBottomSheetVisible이 true로 남아 복귀 시 시트가 열린 상태로 보일 수 있습니다. 사이드이펙트 전송 후 시트를 닫은 상태를 반환하세요.

             is RoutineListIntent.OnApplyTodayClick -> {
                 val selectedRoutine = state.selectedRoutine
                 if (selectedRoutine != null) {
                     sendSideEffect(
                         RoutineListSideEffect.NavigateToWriteRoutine(
                             routineId = selectedRoutine.routineId,
                             applyDate = "TODAY",
                         ),
                     )
                 }
-                null
+                state.copy(editConfirmBottomSheetVisible = false)
             }
 
             is RoutineListIntent.OnApplyTomorrowClick -> {
                 val selectedRoutine = state.selectedRoutine
                 if (selectedRoutine != null) {
                     sendSideEffect(
                         RoutineListSideEffect.NavigateToWriteRoutine(
                             routineId = selectedRoutine.routineId,
                             applyDate = "TOMORROW",
                         ),
                     )
                 }
-                null
+                state.copy(editConfirmBottomSheetVisible = false)
             }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ca58fc2 and 5000a6c.

📒 Files selected for processing (40)
  • app/src/main/java/com/threegap/bitnagil/MainNavHost.kt (3 hunks)
  • app/src/main/java/com/threegap/bitnagil/Route.kt (1 hunks)
  • app/src/main/java/com/threegap/bitnagil/navigation/home/HomeNavHost.kt (2 hunks)
  • core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/color/BitnagilColors.kt (1 hunks)
  • core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/color/Color.kt (1 hunks)
  • core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilTextButton.kt (1 hunks)
  • core/designsystem/src/main/res/drawable/ic_connect.xml (1 hunks)
  • core/designsystem/src/main/res/drawable/ic_edit.xml (1 hunks)
  • core/designsystem/src/main/res/drawable/ic_grow.xml (1 hunks)
  • core/designsystem/src/main/res/drawable/ic_outside.xml (1 hunks)
  • core/designsystem/src/main/res/drawable/ic_rest.xml (1 hunks)
  • core/designsystem/src/main/res/drawable/ic_shine.xml (1 hunks)
  • core/designsystem/src/main/res/drawable/ic_trash.xml (1 hunks)
  • core/designsystem/src/main/res/drawable/ic_wakeup.xml (1 hunks)
  • data/src/main/java/com/threegap/bitnagil/data/routine/datasource/RoutineRemoteDataSource.kt (1 hunks)
  • data/src/main/java/com/threegap/bitnagil/data/routine/datasourceImpl/RoutineRemoteDataSourceImpl.kt (1 hunks)
  • data/src/main/java/com/threegap/bitnagil/data/routine/repositoryImpl/RoutineRepositoryImpl.kt (1 hunks)
  • data/src/main/java/com/threegap/bitnagil/data/routine/service/RoutineService.kt (2 hunks)
  • domain/src/main/java/com/threegap/bitnagil/domain/routine/model/RecommendedRoutineType.kt (1 hunks)
  • domain/src/main/java/com/threegap/bitnagil/domain/routine/model/Routine.kt (2 hunks)
  • domain/src/main/java/com/threegap/bitnagil/domain/routine/repository/RoutineRepository.kt (1 hunks)
  • domain/src/main/java/com/threegap/bitnagil/domain/routine/usecase/DeleteRoutineByDayUseCase.kt (0 hunks)
  • domain/src/main/java/com/threegap/bitnagil/domain/routine/usecase/DeleteRoutineForDayUseCase.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/home/HomeScreen.kt (3 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/home/HomeViewModel.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/home/model/HomeIntent.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/home/model/HomeSideEffect.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/RoutineListScreen.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/RoutineListViewModel.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/component/template/DeleteConfirmBottomSheet.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/component/template/EditConfirmBottomSheet.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/component/template/EmptyRoutineListView.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/component/template/RoutineDetailsCard.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/component/template/WeeklyDatePicker.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/model/DayRoutinesUiModel.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/model/RoutineListIntent.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/model/RoutineListSideEffect.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/model/RoutineListState.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/model/RoutineUiModel.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/model/RoutinesUiModel.kt (1 hunks)
💤 Files with no reviewable changes (1)
  • domain/src/main/java/com/threegap/bitnagil/domain/routine/usecase/DeleteRoutineByDayUseCase.kt
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-07-23T13:32:26.263Z
Learnt from: l5x5l
PR: YAPP-Github/Bitnagil-Android#41
File: presentation/src/main/java/com/threegap/bitnagil/presentation/mypage/model/MyPageIntent.kt:6-6
Timestamp: 2025-07-23T13:32:26.263Z
Learning: In the Bitnagil Android project's MVI architecture, Intent classes like `LoadMyPageSuccess` are named to represent successful API response results that carry loaded data, not just user actions. This naming convention is used for future API integration where the intent will be triggered when my page data loading succeeds from the server.

Applied to files:

  • presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/model/RoutineListIntent.kt
📚 Learning: 2025-07-21T10:38:49.104Z
Learnt from: l5x5l
PR: YAPP-Github/Bitnagil-Android#38
File: presentation/src/main/java/com/threegap/bitnagil/presentation/writeroutine/component/atom/textbutton/TextButton.kt:30-35
Timestamp: 2025-07-21T10:38:49.104Z
Learning: presentation/src/main/java/com/threegap/bitnagil/presentation/writeroutine/component/atom/textbutton/TextButton.kt의 TextButton 컴포넌트는 임시로 구현된 컴포넌트로, 디자인 시스템 구현시 대체 예정입니다.

Applied to files:

  • core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilTextButton.kt
📚 Learning: 2025-08-13T09:06:19.028Z
Learnt from: wjdrjs00
PR: YAPP-Github/Bitnagil-Android#101
File: presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingViewModel.kt:61-67
Timestamp: 2025-08-13T09:06:19.028Z
Learning: In Android ViewModels, when logout success triggers navigation to a different screen (like NavigateToLogin), the current ViewModel's lifecycle ends, so loading states don't need to be explicitly reset in the success case since the ViewModel will be destroyed.

Applied to files:

  • presentation/src/main/java/com/threegap/bitnagil/presentation/home/HomeScreen.kt
🧬 Code Graph Analysis (8)
presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/RoutineListScreen.kt (5)
presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/component/template/DeleteConfirmBottomSheet.kt (1)
  • DeleteConfirmBottomSheet (21-55)
presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/component/template/EditConfirmBottomSheet.kt (1)
  • EditConfirmBottomSheet (21-65)
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/block/BitnagilTopBar.kt (1)
  • BitnagilTopBar (22-63)
presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/component/template/EmptyRoutineListView.kt (1)
  • EmptyRoutineListView (20-65)
presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/component/template/RoutineDetailsCard.kt (1)
  • RoutineDetailsCard (29-141)
presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/component/template/EditConfirmBottomSheet.kt (1)
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilTextButton.kt (1)
  • BitnagilTextButton (34-85)
presentation/src/main/java/com/threegap/bitnagil/presentation/home/HomeViewModel.kt (1)
presentation/src/main/java/com/threegap/bitnagil/presentation/common/mviviewmodel/MviViewModel.kt (1)
  • sendSideEffect (23-23)
presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/component/template/RoutineDetailsCard.kt (1)
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilIcon.kt (2)
  • BitnagilIcon (21-33)
  • BitnagilIconButton (35-58)
presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/component/template/DeleteConfirmBottomSheet.kt (1)
core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/component/atom/BitnagilTextButton.kt (1)
  • BitnagilTextButton (34-85)
presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/RoutineListViewModel.kt (2)
data/src/main/java/com/threegap/bitnagil/data/routine/service/RoutineService.kt (2)
  • fetchRoutines (14-40)
  • fetchRoutines (15-19)
presentation/src/main/java/com/threegap/bitnagil/presentation/common/mviviewmodel/MviViewModel.kt (2)
  • sendSideEffect (23-23)
  • sendIntent (30-37)
data/src/main/java/com/threegap/bitnagil/data/routine/datasourceImpl/RoutineRemoteDataSourceImpl.kt (1)
data/src/main/java/com/threegap/bitnagil/data/common/SafeApiCall.kt (2)
  • safeApiCall (10-25)
  • safeUnitApiCall (27-35)
app/src/main/java/com/threegap/bitnagil/MainNavHost.kt (1)
presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/RoutineListScreen.kt (1)
  • RoutineListScreenContainer (36-92)
🪛 detekt (1.23.8)
domain/src/main/java/com/threegap/bitnagil/domain/routine/model/Routine.kt

[warning] 29-29: The caught exception is swallowed. The original exception could be lost.

(detekt.exceptions.SwallowedException)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (34)
presentation/src/main/java/com/threegap/bitnagil/presentation/home/model/HomeIntent.kt (1)

19-19: 연결 확인 완료: OnShowMoreRoutinesClick 인텐트 → 사이드이펙트 → 네비게이션 정상 동작

아래 경로에서 의도한 흐름이 모두 확인되었습니다:

  • HomeScreen.kt (라인 97): viewModel.sendIntent(HomeIntent.OnShowMoreRoutinesClick)
  • HomeViewModel.kt (라인 123–127): is HomeIntent.OnShowMoreRoutinesClick 분기에서 NavigateToRoutineList 사이드이펙트 발행
  • HomeSideEffect.kt: data class NavigateToRoutineList(val selectedDate: String)
  • MainNavHost.kt (라인 121–127): navigateToRoutineList 람다에서 Route.RoutineList(selectedDate) 네비게이션 호출

👍 모든 연결이 올바르게 구현되어 있어 추가 확인 불필요합니다.

core/designsystem/src/main/res/drawable/ic_edit.xml (1)

2-5: 아이콘 사이즈 24→20dp 변경 — 소비처에서 크기 일관성 보장되는지 확인

디자인 시스템 내에서 20dp/24dp가 혼재하면, 인트린식 크기에 의존하는 화면에서 크기 불균형이 생길 수 있습니다. 모든 사용처가 Modifier.size(...) 등으로 명시 크기를 부여하는지 확인 부탁드립니다. 일괄 20dp 정책이면 다른 아이콘도 순차 정리 대상입니다.

data/src/main/java/com/threegap/bitnagil/data/routine/datasource/RoutineRemoteDataSource.kt (1)

7-13: 레거시 deleteRoutineByDay 메서드 참조 없음 확인

지금까지 실행한 rg -nP '\bdeleteRoutineByDay\s*\(' -g '!**/build/**' 스크립트에서 호출·선언 흔적이 전혀 검색되지 않습니다.
추가로 IDE(또는 전체 텍스트 검색)로 한번 더 확인하신 후 최종 마무리 부탁드립니다.

domain/src/main/java/com/threegap/bitnagil/domain/routine/model/RecommendedRoutineType.kt (1)

14-16: fromString null 반환값 안전 처리 확인됨 — 추가 조치 불필요

  • RoutineDto.kt(Line 41)에서 nullable 프로퍼티로 매핑되어 있어 NPE 발생하지 않음
  • RoutineDetailsCard.kt(Lines 51–55)에서 recommendedRoutineType != null 체크 후 getIcon()/getColor() 호출
  • 별도의 강제 단언(!!)이나 직접 호출 패턴 없음
core/designsystem/src/main/res/drawable/ic_trash.xml (1)

7-7: AI 요약과 불일치: fillColor가 투명으로 바뀌었다고 했으나, 코드상 #989BA2입니다

요약에는 첫 번째 path의 fillColor가 #00000000으로 변경되었다고 되어 있으나, 현재 파일(Line 7)은 #989BA2입니다. 의도된 결과가 무엇인지 확인이 필요합니다.

디자인 가이드(채움 제거하고 stroke만 사용하는 일관 스타일)를 따르려면 첫 path를 제거하거나 투명 처리하고 stroke path만 유지하는지 결정해 주세요.

core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/color/Color.kt (1)

14-14: 팔레트 확장(Yellow10) 추가 LGTM

디자인 시스템 토큰 일관성과 네이밍 컨벤션에 맞습니다. BitnagilColors에 노출도 함께 반영되어 있어 사용성 좋습니다.

core/designsystem/src/main/java/com/threegap/bitnagil/designsystem/color/BitnagilColors.kt (1)

18-18: yellow10 공개 토큰 추가 LGTM

Color.kt의 Yellow10과 정합성도 맞고, 기본값 주입으로 테마 구성에 영향 없습니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/model/DayRoutinesUiModel.kt (1)

12-15: 도메인 → UI 매핑 로직 명확하고 안전합니다

단순 매핑으로 가독성이 좋고, 기본값 처리도 안전합니다. 현재 도메인 스키마에 맞게 잘 구현되었습니다.

app/src/main/java/com/threegap/bitnagil/navigation/home/HomeNavHost.kt (2)

44-45: 새 네비게이션 인자 추가 방향 OK

Home → RoutineList 진입을 위해 navigateToRoutineList: (String) -> Unit 추가한 방향 적절합니다. 외부 NavHost로 안전하게 콜백을 전달할 수 있습니다.


70-72: 콜백 전달 일관성 확인 완료

HomeScreenContainer의 시그니처
• navigateToRegisterRoutine: () -> Unit
• navigateToEmotion: () -> Unit
• navigateToRoutineList: (String) -> Unit

및 HomeNavHost 내 호출부 래핑 로직이 모두 일치함을 확인했습니다. 추가 수정 없이 그대로 진행해 주세요.

presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/model/RoutineListSideEffect.kt (1)

6-6: data object 호환성 확인 완료

  • libs.versions.toml에서 kotlin = "2.1.20"로 정의되어 있습니다.
  • 해당 버전(2.1.0 이상)에서 data object 문법을 지원하므로 별도 변경이 필요 없습니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/model/RoutinesUiModel.kt (1)

14-16: 매핑 구현은 문제 없습니다

도메인 Map의 값을 UI 모델로 변환하는 구현이 간결합니다. 순서 보존(Map 구현체)에 의존하지 않는다면 현재 방식으로 충분합니다.

data/src/main/java/com/threegap/bitnagil/data/routine/repositoryImpl/RoutineRepositoryImpl.kt (2)

25-30: 서버 API 변경 반영 OK (id 기반 삭제/일자 삭제 분리)

  • 전체 삭제: deleteRoutine(routineId)
  • 특정 일자 삭제: deleteRoutineForDay(routineId)

RemoteDataSource 위임도 일관적입니다.


25-30: 추가 검증 요청: 구/신 API 사용 흔적 확인
스크립트 실행 시 deleteRoutineByDay 또는 RoutineByDayDeletion 호출 흔적은 검색되지 않았으나, 결과가 완전하지 않을 수 있어 수동 확인이 필요합니다. 아래 사항을 검토해 주세요:

  • deleteRoutineByDayRoutineByDayDeletion 잔여 여부
  • deleteRoutineForDay 호출처 전체
  • RoutineRemoteDataSource 인터페이스와 RoutineRemoteDataSourceImpl, 관련 Service 구현체에 deleteRoutineForDay 메서드 정의 여부

위 항목들을 확인 후 이상이 없으면 머지 진행 바랍니다.

data/src/main/java/com/threegap/bitnagil/data/routine/datasourceImpl/RoutineRemoteDataSourceImpl.kt (1)

25-28: getRoutine API 응답 데이터 존재 여부 확인 필요

safeApiCall은 BaseResponse.data가 null일 경우 바로 실패로 처리합니다.
RoutineService.getRoutine(…)가 항상 data 필드를 채워 응답하는지(예: 204 No Content나 빈 body 없이) 서버 응답 계약을 다시 한번 확인해주세요.

점검 위치:

  • data/src/main/java/com/threegap/bitnagil/data/routine/datasourceImpl/RoutineRemoteDataSourceImpl.kt:26–28 (getRoutine 호출부)
domain/src/main/java/com/threegap/bitnagil/domain/routine/usecase/DeleteRoutineForDayUseCase.kt (1)

9-11: 심플 패스스루 유스케이스, 적절합니다

도메인 계층에서 의미를 드러내는 얇은 래퍼로 Result<Unit> 전달이 일관적입니다.

app/src/main/java/com/threegap/bitnagil/MainNavHost.kt (2)

122-128: Home → RoutineList 네비게이션 연결 깔끔합니다

launchSingleTop = true로 중복 스택 방지까지 고려되어 있어 적절합니다.


253-261: RoutineListViewModel에서 selectedDate 파라미터가 정상 처리되고 있습니다

  • presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/RoutineListViewModel.kt
    • 30~33행: savedStateHandle.get<String>("selectedDate")로 파라미터를 받아 LocalDate.parse 후 초기 상태에 반영

Composable 쪽에서 별도로 파라미터를 꺼내 전달하지 않아도, 네비게이션 시 전달된 selectedDate 값이 ViewModel의 SavedStateHandle을 통해 올바르게 초기화됩니다. 따라서 추가 작업은 필요하지 않습니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/component/template/EditConfirmBottomSheet.kt (2)

67-108: 잘 구현된 UI 컴포넌트입니다.

BitnagilTheme 디자인 시스템을 일관되게 사용하고 있으며, 레이아웃과 스타일링이 적절합니다. 컴포지션 구조도 명확하고 재사용 가능한 형태로 잘 설계되어 있습니다.


110-117: Preview 함수가 잘 구현되었습니다.

개발 중 UI 확인을 위한 Preview 함수가 적절히 구현되어 있어 개발 효율성을 높일 수 있습니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/model/RoutineListState.kt (1)

8-22: 잘 설계된 MVI 상태 클래스입니다.

MVI 패턴에 맞게 상태를 잘 정의했고, computed properties를 통해 UI에 필요한 데이터를 효율적으로 제공하고 있습니다. @Parcelize 애노테이션을 통한 직렬화 지원도 적절합니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/component/template/DeleteConfirmBottomSheet.kt (4)

21-55: 조건부 렌더링 로직이 명확합니다.

isRepeatRoutine 플래그에 따라 적절한 컨텐츠를 렌더링하는 구조가 잘 설계되어 있습니다. 각 케이스에 맞는 UI를 제공하고 있어 사용자 경험을 고려한 좋은 구현입니다.


57-102: 반복 루틴 삭제 UI가 잘 구현되었습니다.

사용자에게 명확한 옵션을 제공하고, 삭제의 불가역성을 적절히 경고하고 있습니다. BitnagilTextButtonColor.delete() 사용으로 위험한 액션임을 시각적으로도 잘 표현했습니다.


104-151: 단일 루틴 삭제 UI가 잘 구현되었습니다.

취소/삭제 버튼을 수평으로 배치하고 적절한 색상 구분을 통해 사용자가 쉽게 선택할 수 있도록 구현되었습니다. Row의 weight(1f) 사용으로 버튼 크기가 균등하게 배분되는 것도 좋습니다.


153-171: Preview 함수들이 적절히 구현되었습니다.

두 가지 삭제 시나리오에 대한 Preview를 각각 제공하여 개발 시 UI 확인이 용이하도록 했습니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/model/RoutineListIntent.kt (1)

6-18: MVI 패턴에 맞는 잘 설계된 Intent 클래스입니다.

루틴 리스트 기능에 필요한 모든 사용자 액션과 상태 변경을 포괄적으로 정의했습니다. Intent 이름들이 직관적이고 각각의 역할이 명확합니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/model/RoutineUiModel.kt (4)

13-22: UI 모델이 잘 설계되었습니다.

필요한 모든 필드를 포함하고 있고, @Parcelize를 통한 직렬화 지원으로 화면 전환 시 데이터 전달이 원활할 것입니다. nullable한 recommendedRoutineType 처리도 적절합니다.


24-33: 도메인-UI 변환 로직이 잘 구현되었습니다.

도메인 모델의 Routine을 UI 모델로 변환하는 확장 함수가 간결하고 명확합니다. formattedExecutionTime 프로퍼티를 사용하여 UI에 적합한 형태로 시간 정보를 제공하는 것도 좋습니다.


35-44: 아이콘 매핑이 완전합니다.

모든 RecommendedRoutineType 케이스에 대한 아이콘을 정의하여 누락 없이 처리했습니다. when 식이 exhaustive하여 컴파일 타임에 안전성을 보장합니다.


46-56: 색상 매핑이 일관성 있게 구현되었습니다.

BitnagilTheme의 색상을 사용하여 디자인 시스템과 일관성을 유지하고 있습니다. @Composable 애노테이션으로 Compose 환경에서만 호출됨을 명시한 것도 적절합니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/home/HomeScreen.kt (1)

44-46: HomeScreenContainer 호출부 검증 완료

현재 HomeScreenContainer는 호출부에서 모두 명명된(named) 인자를 사용하고 있어 파라미터 순서 변경의 영향이 없습니다. 추가 검증이나 수정이 필요 없으므로 해당 이슈는 해결된 것으로 보입니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/RoutineListViewModel.kt (1)

30-36: selectedDate 파싱 포맷 확인

SavedStateHandle로 전달되는 selectedDate가 LocalDate.parse 가능한 ISO-8601(yyyy-MM-dd) 형식임을 보장해야 합니다. 라우팅 인코딩/디코딩 과정에서 포맷이 달라지지 않았는지 점검 부탁드립니다.

data/src/main/java/com/threegap/bitnagil/data/routine/service/RoutineService.kt (2)

21-25: 단일 루틴 조회(getRoutine) 추가 적합

단일 루틴 조회 엔드포인트 추가가 명확하고, DTO/응답 타입 매핑도 적절합니다.


36-39: 일자 삭제 엔드포인트 전환 적합

일자 삭제가 v2 경로(/api/v2/routines/day/{routineId})로 전환된 부분이 서비스 시그니처에 반영되었습니다. 도메인/레포지토리 계층과도 일관적입니다.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
presentation/src/main/java/com/threegap/bitnagil/presentation/home/util/LocalDateExtension.kt (3)

27-37: 24시간 포맷에서 자정 텍스트만 개행(“하루\n종일”)으로 표시되어 일관성 깨짐 — 요약과도 불일치

12시간 포맷은 "하루종일"(한 줄)인데, 24시간 포맷만 두 줄로 표시되어 리스트 아이템 높이가 들쭉날쭉해질 수 있습니다. AI 요약에도 두 포맷 모두 한 줄로 출력된다고 되어 있어 불일치합니다. 한 줄 표기로 통일하는 것을 권장합니다.

권장 수정안:

-            "하루\n종일"
+            "하루종일"

27-37: 예외 범위가 너무 넓음 — DateTimeParseException으로 한정 권장

catch (e: Exception)은 다른 프로그래밍 오류까지 삼킬 수 있습니다. 파싱 실패만 처리하도록 DateTimeParseException으로 좁히는 편이 안전합니다.

적용 diff:

-    } catch (e: Exception) {
+    } catch (e: java.time.format.DateTimeParseException) {
         "시간 미정"
     }

(필요 시 파일 상단에 import java.time.format.DateTimeParseException 추가)


1-10: 파일명과 책임 혼재 — 확장 대상 기준으로 파일 분리/리네임 제안

LocalDateExtension 파일에 LocalDate/String 확장이 함께 존재합니다. 유지보수성을 위해 DateExtensions.ktTimeStringExtensions.kt 등으로 분리하거나 보다 중립적인 DateTimeExtensions.kt로 리네임을 고려해 주세요.

presentation/src/main/java/com/threegap/bitnagil/presentation/home/component/template/RoutineSection.kt (1)

28-33: 자정 케이스가 2줄로 내려갈 경우(“하루\n종일”) 레이아웃 요동 가능성

현 시점 24시간 포맷이 두 줄을 반환하면 Row 높이가 커지고 아이템 간 정렬이 틀어질 수 있습니다. 앞서 제안드린 대로 "하루종일"(한 줄)로 통일하면 본 문제는 자연히 해소됩니다. 만약 2줄 표기를 의도한 것이라면, Text(maxLines = 2) 등의 명시적 제약과 고정 폭(예: "하루종일"을 수용 가능한 minWidth) 재조정 검토를 권장합니다.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 5000a6c and 005e5d4.

📒 Files selected for processing (4)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/home/component/template/RoutineSection.kt (2 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/home/util/LocalDateExtension.kt (3 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/RoutineListViewModel.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/model/RoutineUiModel.kt (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/model/RoutineUiModel.kt
  • presentation/src/main/java/com/threegap/bitnagil/presentation/routinelist/RoutineListViewModel.kt
🔇 Additional comments (4)
presentation/src/main/java/com/threegap/bitnagil/presentation/home/util/LocalDateExtension.kt (3)

12-13: 12시간 포맷터 추가 적절 (로케일/패턴 OK)

"a h:mm" 패턴과 Locale.KOREAN 조합으로 "오전/오후 h:mm" 포맷이 기대대로 출력됩니다.


39-49: 12시간 포맷 함수 로직은 적절

  • 자정 처리: "하루종일"(한 줄) — OK
  • 정상 시각: "a h:mm" 포맷 — OK
  • 파싱 실패: "시간 미정" — OK

27-49: formatExecutionTime 호출 마이그레이션 완료 확인
기존 formatExecutionTime() 호출이 더 이상 남아있지 않으며, 모든 호출이 formatExecutionTime24Hour/formatExecutionTime12Hour로 성공적으로 대체되었습니다.
– 더 이상 추가 확인이 필요 없습니다.

presentation/src/main/java/com/threegap/bitnagil/presentation/home/component/template/RoutineSection.kt (1)

15-15: 신규 24시간 포맷 확장함수로의 import 변경 적절

UI 일관성을 위해 24시간 포맷을 사용하는 의도와 부합합니다.

Copy link
Copy Markdown
Contributor

@l5x5l l5x5l left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생 많으셨습니다! 머지 진행하시죠!

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
presentation/src/main/java/com/threegap/bitnagil/presentation/home/HomeScreen.kt (2)

65-68: 내비게이션 사이드 이펙트 처리 추가 — OK. 다만 타입을 LocalDate로 유지하는 방안을 고려해 주세요

현재 navigateToRoutineList 콜백이 String을 받습니다. 날짜 문자열 포맷(예: yyyy-MM-dd) 일관성 문제가 숨어들기 쉬우므로, 프레젠테이션 계층에서는 LocalDate를 유지하고 실제 라우팅 직전에만 String으로 변환하는 편이 안전합니다. 이로 인해 도메인/프레젠테이션 경계에서의 형식 오류를 줄일 수 있습니다.

선택적 리팩터 제안:

  • HomeSideEffect.NavigateToRoutineList의 selectedDateLocalDate로 유지
  • 본 컨테이너의 콜백 시그니처를 (LocalDate) -> Unit으로 변경
  • 상위 NavHost에서 실제 Route로 변환 시 selectedDate.format(DateTimeFormatter.ISO_LOCAL_DATE)로 문자열화

105-107: 불필요한 괄호 제거

이중 괄호는 불필요합니다. 간결하게 유지해 주세요.

-            viewModel.sendIntent((HomeIntent.OnShowMoreRoutinesClick))
+            viewModel.sendIntent(HomeIntent.OnShowMoreRoutinesClick)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2034630 and 6c44185.

📒 Files selected for processing (7)
  • app/src/main/java/com/threegap/bitnagil/MainNavHost.kt (3 hunks)
  • app/src/main/java/com/threegap/bitnagil/Route.kt (1 hunks)
  • app/src/main/java/com/threegap/bitnagil/navigation/home/HomeNavHost.kt (2 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/home/HomeScreen.kt (3 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/home/HomeViewModel.kt (2 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/home/model/HomeIntent.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/home/model/HomeSideEffect.kt (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/home/model/HomeSideEffect.kt
  • presentation/src/main/java/com/threegap/bitnagil/presentation/home/model/HomeIntent.kt
  • app/src/main/java/com/threegap/bitnagil/Route.kt
  • presentation/src/main/java/com/threegap/bitnagil/presentation/home/HomeViewModel.kt
  • app/src/main/java/com/threegap/bitnagil/MainNavHost.kt
  • app/src/main/java/com/threegap/bitnagil/navigation/home/HomeNavHost.kt
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (1)
presentation/src/main/java/com/threegap/bitnagil/presentation/home/HomeScreen.kt (1)

41-48: HomeScreenContainer 함수에서 중복된 viewModel 파라미터 제거

첫 번째 viewModel: HomeViewModel = hiltViewModel() 선언을 제거했습니다.
호출부는 모두 named argument를 사용하고 있어 파라미터 순서 변경에 따른 빌드 오류가 없습니다.

적용 제안:

 @Composable
 fun HomeScreenContainer(
-    viewModel: HomeViewModel = hiltViewModel(),
     navigateToGuide: () -> Unit,
     navigateToRegisterRoutine: () -> Unit,
     navigateToEmotion: () -> Unit,
     navigateToRoutineList: (String) -> Unit,
     viewModel: HomeViewModel = hiltViewModel(),
 )

확인된 호출부:

  • app/src/main/java/com/threegap/bitnagil/navigation/home/HomeNavHost.kt (라인 66–73): 모든 인자를 named argument로 전달 중

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
presentation/src/main/java/com/threegap/bitnagil/presentation/home/HomeScreen.kt (2)

64-66: 사이드이펙트 → 네비게이션 연결은 적절함. 날짜 문자열 포맷/인코딩만 확인 필요

HomeSideEffect.NavigateToRoutineList를 받아 navigateToRoutineList(sideEffect.selectedDate)로 위임하는 흐름은 자연스럽습니다. 다만 selectedDate가 라우트 파라미터로 사용된다면:

  • 앱 전역에서 날짜 포맷을 ISO_LOCAL_DATE 등으로 일관화했는지
  • Nav 인자에 안전하게 전달(특수문자 포함 시 인코딩)되는지

를 확인해 주세요. 필요하다면 VM에서 포맷을 보장한 뒤 넘기거나, 라우팅 직전에 인코딩을 적용하는 방식을 권장합니다. 도메인적으로는 컨테이너 레벨에서 String 대신 LocalDate를 받고, 라우팅 계층에서만 문자열로 변환하는 설계도 고려할 수 있습니다.


105-105: 불필요한 괄호 제거 (가독성 개선)

중첩 괄호가 불필요합니다. 아래처럼 정리하는 것이 가독성에 좋습니다.

-            viewModel.sendIntent((HomeIntent.OnShowMoreRoutinesClick))
+            viewModel.sendIntent(HomeIntent.OnShowMoreRoutinesClick)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6c44185 and dc6cac2.

📒 Files selected for processing (2)
  • app/src/main/java/com/threegap/bitnagil/Route.kt (1 hunks)
  • presentation/src/main/java/com/threegap/bitnagil/presentation/home/HomeScreen.kt (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/main/java/com/threegap/bitnagil/Route.kt
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-13T09:06:19.028Z
Learnt from: wjdrjs00
PR: YAPP-Github/Bitnagil-Android#101
File: presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingViewModel.kt:61-67
Timestamp: 2025-08-13T09:06:19.028Z
Learning: In Android ViewModels, when logout success triggers navigation to a different screen (like NavigateToLogin), the current ViewModel's lifecycle ends, so loading states don't need to be explicitly reset in the success case since the ViewModel will be destroyed.

Applied to files:

  • presentation/src/main/java/com/threegap/bitnagil/presentation/home/HomeScreen.kt
🔇 Additional comments (1)
presentation/src/main/java/com/threegap/bitnagil/presentation/home/HomeScreen.kt (1)

45-47: HomeScreenContainer 호출부 점검 결과: 명명 인자로 전달되어 컴파일 브레이킹 없음

HomeNavHost.kt에서 HomeScreenContainer를 호출할 때 모든 콜백 파라미터를 명명 인자(named arguments)로 전달하고 있으며, viewModel은 기본값(default)을 사용합니다. 따라서 파라미터 순서가 변경되더라도 컴파일 오류는 발생하지 않습니다.

  • 호출부 확인:
    • app/src/main/java/com/threegap/bitnagil/navigation/home/HomeNavHost.kt (줄 66–74)

@wjdrjs00 wjdrjs00 merged commit a29f624 into develop Aug 18, 2025
2 checks passed
@wjdrjs00 wjdrjs00 deleted the feature/#108-routine-list branch August 18, 2025 14:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 새로운 기능 구현 🧤 대현

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] 루틴 리스트 화면을 구현합니다.

2 participants